/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CrSyncFilesForms.Properties;
using CrSyncFilesForms.Utils;
using CrSyncFilesUtils;

namespace CrSyncFilesForms
{

    /// <summary>
    /// UI for displaying visual graphic like a rotating preloader while executing a backtround thread.
    /// This is helpful if you want to inform the user that there is something in the background that is currently running but
    /// unable to show the current progress.
    /// </summary>
    public partial class CrSyncUiPreLoaderDialog : Form
    {
        public CrSyncUiPreLoaderDialog()
        {
            InitializeComponent();
            Load += CallBackLoad;

            BackgroundThread = new BackgroundWorker {WorkerSupportsCancellation = true, WorkerReportsProgress = true};

            Shown += CallBackShown;

            Timeout = 10;
            TimeBeforeAutoCancel = 0;
            TimeBeforeForceCancel = 5;
            _counterTimeout = 0;
            _counterAutoCancel = 0;
            _counterForceCancel = 0;

            TimeoutMessage = Resources.MESSAGE_TIMEOUT;

            ButtonClose.Visible = false;

            
        }


        public string StatusMessage
        {
            get { return UIPreLoader.StatusMessage; }
            set { UIPreLoader.StatusMessage = value; }
        }

        public object BackgroundThreadArguments { get; set; }

        public BackgroundWorker BackgroundThread { get; set; }

        public CrSyncEventHandler DebugEvent { get; set; }

        /// <summary>
        /// Triggers when the close button is pressed
        /// </summary>
        public EventHandler CancelEvent;

        /// <summary>
        /// When the specified time out counter is reached
        /// </summary>
        public EventHandler TimeoutEvent;


        /// <summary>
        /// Triggers when this window closes due to error or when timeout is reached.
        /// </summary>
        public EventHandler ErrorOnClose;
        

        protected virtual  void OnErrorOnClose(EventArgs args)
        {
            if(ErrorOnClose != null)
            {
                ErrorOnClose(this, args);
            }
        }

        protected virtual void OnTimeoutEvent(EventArgs args)
        {
            if(TimeoutEvent != null)
            {
                TimeoutEvent(this, args);
            }
        }

        protected virtual void OnDebugEvent( CrSyncEventArgs args)
        {
            if(DebugEvent != null)
            {
                DebugEvent(this, args);
            }
        }
        
        /// <summary>
        /// If set to true, will show the close button when Timeout is triggered.
        /// </summary>
        public bool ShowCloseButton { get; set; }

        /// <summary>
        /// Time at which before the time out message is shown and before the TimeOutEvent is triggered.
        /// Default is 10 seconds.
        /// </summary>
        public int Timeout
        {
            get { return _timeout; }
            set
            {
                _timeoutEnabled = value > 0;
                _timeout = value;
            }
        }

        /// <summary>
        /// The number of seconds before the auto cancel will be executed on the running thread.
        /// If set to greater than zero, will automatically execute auto cancel after reaching the specified Timeout.
        /// </summary>
        public int TimeBeforeAutoCancel
        {
            get { return _autoCancel; }
            set
            {
                _autoCancelEnabled = value > 0;
                _autoCancel = value;
            }
        }

        /// <summary>
        /// Time in seconds before trying to auto cancel the thread.
        /// </summary>
        public int TimeBeforeForceCancel
        {
            get { return _forceCancel; }
            set
            {
                _forceCancelEnabled = value > 0;
                _forceCancel = value;
            }
        }

        /// <summary>
        /// Timeout message to use. Will appear if Timeout value is greater than 0.
        /// </summary>
        public string TimeoutMessage { get; set; }

        private int _timeout;
        private int _autoCancel;
        private int _forceCancel;
        private Timer _timerTimeout; // Timer to count before we declare it's a timeout
        private Timer _timerAutoCancel; // Timer to count before proceeding to auto cancel the running thread.
        private Timer _timerForceCancel; // Timer to count before forcing the running background thread to cancel without cutting off the thread directly. This timer is invoked only when user explicitly instruct this window to close and quit.
        private int _counterTimeout; 
        private int _counterAutoCancel;
        private int _counterForceCancel;
        private bool _timeoutEnabled, _autoCancelEnabled, _forceCancelEnabled;

        protected virtual  void CallBackLoad(object sender, EventArgs args)
        {
            ButtonClose.Click += CancelEvent;


            var message = "Pre-loader configuration:\r\n\r\n\r\n" +
                                "TIMEOUT ENABLED: " + _timeoutEnabled + " VALUE: " + _timeout + "\r\n\r\n" +
                                "AUTO CANCEL ENABLED: " + _autoCancelEnabled + " VALUE: " + _autoCancel + "\r\n\r\n" +
                                "FORCE CANCEL ENABLED: " + _forceCancelEnabled + " VALUE: " + _forceCancel;

            OnDebugEvent(new CrSyncEventArgs(message));
        }

        protected virtual void CallBackShown(object sender, EventArgs args)
        {
            OnDebugEvent(new CrSyncEventArgs("Pre-loader: UI loaded..."));
            // Start counting for timeouts only if the specified TimeOut is not 0
            if(_timeoutEnabled)
            {
                _timerTimeout = new Timer { Interval = 1000 };
                _timerTimeout.Tick += CallBackTimeout;
                _timerTimeout.Start();
                OnDebugEvent(new CrSyncEventArgs("Pre-loader: timeout counter started..."));
            }
            

            // Prepare counter for auto cancel
            _timerAutoCancel = new Timer { Interval = 1000 };
            _timerAutoCancel.Tick += CallBackAutoCancel;

            // Prepare counter for forece cancel
            _timerForceCancel = new Timer{ Interval = 1000};
            _timerForceCancel.Tick += CallBackForceCancel;

            if(BackgroundThreadArguments != null)
            {
                OnDebugEvent(new CrSyncEventArgs("Pre-loader: Starting thread with arguments..."));
                BackgroundThread.RunWorkerAsync(BackgroundThreadArguments);
            }
            else
            {
                OnDebugEvent(new CrSyncEventArgs("Pre-loader: Starting thread without arguments..."));
                BackgroundThread.RunWorkerAsync();
            }
        }

        protected virtual void CallBackTimeout(object  sender, EventArgs args)
        {
            if(_timeoutEnabled)
            {
                if (_counterTimeout == Timeout)
                {

                    _timerTimeout.Stop();
                    _timeoutEnabled = false;

                    if (ShowCloseButton)
                    {
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: showing the close button..."));
                        ButtonClose.Show();
                    }

                    UIPreLoader.StatusMessage = TimeoutMessage;

                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: Triggering timeout event"));
                    // Fire a Timeout event
                    OnTimeoutEvent(new EventArgs());

                    // Start the timer to close the running thread if TimeBeforeClose is greater than 0
                    if (_autoCancelEnabled)
                    {
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: Starting autocancel..."));
                        _timerAutoCancel.Start();
                    }


                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: Stopping timeout handler..."));
                }
                else if(_counterTimeout < Timeout)
                {
                    _counterTimeout++;
                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: preparing for timeout... counter value is "+_counterTimeout));
                }
                else
                {
                    _timerTimeout.Stop();
                    _timeoutEnabled = false;
                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: Stopping timeout handler..."));
                }
            }
            else
            {
                _timerTimeout.Stop();
                _timeoutEnabled = false;
                OnDebugEvent(new CrSyncEventArgs("Pre-loader: Stopping timeout handler..."));
            }

        }

        protected virtual void CallBackAutoCancel(object sender, EventArgs args)
        {
            if(_autoCancelEnabled)
            {
                if(_counterAutoCancel == TimeBeforeAutoCancel)
                {
                    if (BackgroundThread.CancellationPending)
                    {
                        ClosePreloader();
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: the running thread is pending and will now auto cancel and will trigger error event..."));
                        OnErrorOnClose(new EventArgs());
                        _timerAutoCancel.Stop();
                        _autoCancelEnabled = false;
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: the running thread will now close..."));
                        
                    }
                    else if (BackgroundThread.IsBusy)
                    {
                        BackgroundThread.CancelAsync();
                        _counterAutoCancel = 0;
                        TimeBeforeAutoCancel = 2;
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: thread is busy will try to auto cancel and check in other 2 seconds..."));
                        // Thread is busy will try to cancel and check it again for the next 2 seconds and close this window
                    }
                    else
                    {
                        ClosePreloader();
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: the running thread will now auto cancel and will trigger error event..."));
                        OnErrorOnClose(new EventArgs());
                        _timerAutoCancel.Stop();
                        _autoCancelEnabled = false;
                        OnDebugEvent(new CrSyncEventArgs("Pre-loader: the running thread will now close..."));
                        
                    }
                }
                else if(_counterAutoCancel < TimeBeforeAutoCancel)
                {
                    _counterAutoCancel++;
                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: Preparing to auto cancel. Counter value is "+_counterAutoCancel));
                }
                else
                {
                    ClosePreloader();
                    _timerAutoCancel.Stop();
                    _autoCancelEnabled = false;
                    OnDebugEvent(new CrSyncEventArgs("Pre-loader: Thread is now cancelled and pre-loader dialog show now be closed..."));
                }
            }
            else
            {
                ClosePreloader();
                _autoCancelEnabled = false;
                _timerAutoCancel.Stop();

            }
        }


        public void CallBackForceCancel(object sender, EventArgs args)
        {
            if(_forceCancelEnabled)
            {
                if (_counterForceCancel == TimeBeforeForceCancel)
                {
                    if (BackgroundThread.CancellationPending)
                    {
                        _forceCancelEnabled = false;
                        _timerForceCancel.Stop();
                        ClosePreloader();
                    }
                    else if (BackgroundThread.IsBusy)
                    {
                        // Thread is the busy, will try to cancel the running thread and check it again before we close this window
                        BackgroundThread.CancelAsync();
                        _counterForceCancel = 0;
                    }
                    else
                    {
                        _forceCancelEnabled = false;
                        _timerForceCancel.Stop();
                        ClosePreloader();
                    }
                }
                else if (_counterForceCancel < TimeBeforeForceCancel)
                {
                    _counterForceCancel++;
                }
                else
                {
                    _forceCancelEnabled = false;
                    _timerForceCancel.Stop();
                    ClosePreloader();
                }
            }
            else
            {
                _forceCancelEnabled = false;
                _timerForceCancel.Stop();
                ClosePreloader();
            }
        }

        public void CancelAndClose()
        {
            try
            {
                TimeBeforeAutoCancel = 0;
                _timerForceCancel.Start();
            }
            catch(Exception exception)
            {
                ApplicationGlobals.SetVerboseMessage(exception.Message);
            }
        }

        public void ClosePreloader()
        {
            TimeBeforeAutoCancel = 0;
            Timeout = 0;
            TimeBeforeForceCancel = 0;
            Close();
        }

        
    }
}
